/*
 * Created by Spear (http://www.codernet.es), spear@codernet.es
 * Coordinator of XNA Community (http://www.codeplex.com/XNACommunity)
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;

using XMas;

namespace XMasContent
{
    public class CCal3DAnimParser
    {
        
        public struct TCalTrack
        {
            public int bone_id;
            public int keyframe_count;
            public List<TCalAnimationKey> keys;
        };

        public struct TCalAnimationKey 
        {
	        public float       time;
	        public Vector3     pos;
	        public Quaternion  rotation;
        };

        List<TCalTrack> list_bones = new List<TCalTrack>();
        Dictionary<int, int> bones_parent_id = new Dictionary<int,int>();
        CSkeletonData skeleton_data;
    
        float frame_rate = 0.0f;
        bool debug_print = true;
        float duration;

    


        bool isValid(char[] magic)
        {
            return (magic[0] == 'C' && magic[1] == 'A' && magic[2] == 'F' && magic[3] == 0x00);
        }

        void loadBonesKeys( BinaryReader reader )
        {
            //Quaternion x_axis_90 = Quaternion.CreateFromAxisAngle(new Vector3(-1.0f, 0.0f, 0.0f), MathHelper.PiOver2);
            //x_axis_90.Normalize();
            TCalTrack track = new TCalTrack();
            track.keys = new List<TCalAnimationKey>();
            track.bone_id = reader.ReadInt32();
            track.keyframe_count = reader.ReadInt32();

            if (list_bones.Count == 0)
                frame_rate = (float)duration / (float)track.keyframe_count;

            for (int i = 0; i < track.keyframe_count; ++i)
            {
                TCalAnimationKey key = new TCalAnimationKey();
                key.time = reader.ReadSingle();
                key.pos = XmasContent.GetInstance().readVector3(reader);
                key.rotation = Quaternion.Inverse(XmasContent.GetInstance().readQuaternion(reader) );
                
                /*
                if (track.bone_id == 0)
                {
                    key.rotation = x_axis_90 * key.rotation;
                    key.pos = Vector3.Transform(key.pos, x_axis_90);
                }
                */

                track.keys.Add(key);
            }

            list_bones.Add(track);
        }

        void loadBonesParentIDs( BoneContent skeleton ) 
        {
            //bones_parent_id
            for (int i = 0; i < skeleton.Children.Count; ++i)
            {
                BoneContent bone = (BoneContent)skeleton.Children[i];
                int bone_id = int.Parse(bone.Name.Substring(4));
                int parent_id = -1;
                if (bone.Parent != null)
                    parent_id = int.Parse(bone.Parent.Name.Substring(4));
                else
                    parent_id = -1;

                bones_parent_id.Add(bone_id, parent_id);
                if( bone.Children.Count > 0 )
                    loadBonesParentIDs(bone);
            }
        }

        int getParentID(int bone_idx)
        {
            if (bones_parent_id.ContainsKey(bone_idx))
                return bones_parent_id[bone_idx];
            return -1;
        }


        public void load(string filename, ContentProcessorContext c, CSkeletonData s, BoneContent skeleton)
        {
            //System.Diagnostics.Debugger.Launch(); 
            skeleton_data = s;
            XmasContent.GetInstance().SetContextProcessor(c);
            loadBonesParentIDs((BoneContent)skeleton);

            FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read);
            BinaryReader reader = new BinaryReader(file);
            char[] magic = new char[4];
            magic = reader.ReadChars(4);
            if (isValid(magic))
            {
                reader.ReadInt32();
                duration = reader.ReadSingle();
                int bones_count = reader.ReadInt32();
                for (int i = 0; i < bones_count; ++i)
                    loadBonesKeys(reader);

                createSkeleton(Path.GetFileNameWithoutExtension(filename).ToLower());
            }
            else
            {
                throw new ArgumentNullException("File: " + filename + ", CMF Version not supported.\n");
            }
            reader.Close();
        }

    
        private CSkeletonData.CFramesData createSkeletonByFrame(int frame_index)
        {
            CSkeletonData.CFramesData frame_data = new CSkeletonData.CFramesData();
            if (debug_print)
            {
                XmasContent.GetInstance().warn("\n\nSkeleton frame info:", false);
            }
            for (int i = 0; i < list_bones.Count; ++i)
            {
                TCalTrack cal_track = list_bones[i];
                TCalAnimationKey key = cal_track.keys[frame_index];
                Vector3 pos = key.pos;
                Quaternion oriented = key.rotation;
                oriented.Normalize();

                int parent = getParentID(i);
                CSkeletonData.CSkeletonNode node = new CSkeletonData.CSkeletonNode();
                node.parent = parent;
                node.pos = pos;
                node.oriented = oriented;
                node.oriented.Normalize();
                
                if (debug_print)
                {
                    XmasContent.GetInstance().warn("bone id: " + frame_data.list_skeleton.Count + " parent id: " + parent, false);
                }
                frame_data.list_skeleton.Add(node);
            }
            debug_print = false;
            return frame_data;
        }

        private void createSkeleton(string name)
        {
            //create animation entry
            CSkeletonData.CAnimationData current_animation = new CSkeletonData.CAnimationData();
            //list_frames
            int keys_count = list_bones[0].keyframe_count;
            for (int i = 0; i < keys_count; ++i)
            {
                CSkeletonData.CFramesData frame = createSkeletonByFrame(i);
                current_animation.list_frames.Add(frame);
            }
            current_animation.frame_rate = frame_rate;
            skeleton_data.addAnimation(name, current_animation);
        }
    }
}
